#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <signal.h>

#include <coap2/coap.h>

#include "coap-resource.h"


#ifdef __GNUC__
#define UNUSED_PARAM __attribute__ ((unused))
#else /* not a GCC */
#define UNUSED_PARAM
#endif /* GCC */

#ifndef WITHOUT_ASYNC
/* This variable is used to mimic long-running tasks that require
 * asynchronous responses. */
static coap_async_state_t *async = NULL;
#endif /* WITHOUT_ASYNC */


static coap_log_t log_level = LOG_INFO;

static int key_defined = 0;
static char *cert_file = NULL; /* Combined certificate and private key in PEM */
/* changeable clock base (see handle_put_time()) */

static time_t clock_offset;
static time_t my_clock_base = 0;

struct coap_resource_t *time_resource = NULL;

static int support_dynamic = 0;
static int resource_flags = COAP_RESOURCE_FLAGS_NOTIFY_CON;


/* temporary storage for dynamic resource representations */
static int quit = 0;


/* SIGINT handler: set quit to 1 for graceful termination */
static void
handle_sigint(int signum UNUSED_PARAM) {
  quit = 1;
}


 /* Create the libcoap context */
coap_context_t * 
create_context() {
    coap_context_t *ctx = NULL;
    if ((ctx = coap_new_context(NULL)) == NULL) {
        return NULL;
    }
}

void
bind_server_addr(const char* Addr, const char* port, coap_context_t *ctx) {
    struct addrinfo hints;
    struct addrinfo *result, *rp;

    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
    hints.ai_socktype = SOCK_DGRAM; /* Coap uses UDP */
    hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;


    /* judjement addrs and port if null or not */
    if (Addr && port) {
      int s = getaddrinfo(Addr, port, &hints, &result);
      if ( s != 0 ) {
          fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
          coap_free_context(ctx);
          return;
      }
    }

    coap_address_t addr, addrs;
    coap_endpoint_t *ep_udp = NULL, *ep_dtls = NULL,
                    *ep_tcp = NULL, *ep_tls = NULL;

    /* iterate through results until success */
    for (rp = result; rp != NULL; rp = rp->ai_next) {

        /* initial socket config */
        if (rp->ai_addrlen <= sizeof(addr.addr)) {
            coap_address_init(&addr);
            addr.size = rp->ai_addrlen;
            memcpy(&addr.addr, rp->ai_addr, rp->ai_addrlen);
            addrs = addr;
            if (addr.addr.sa.sa_family == AF_INET) {
                uint16_t temp = ntohs(addr.addr.sin.sin_port) + 1;
                addrs.addr.sin.sin_port = htons(temp);
            } else if (addr.addr.sa.sa_family == AF_INET6) {
                uint16_t temp = ntohs(addr.addr.sin6.sin6_port) + 1;
                addrs.addr.sin6.sin6_port = htons(temp);
            } else {
                goto finish;
            }

            if ((ep_udp = coap_new_endpoint(ctx, &addr, COAP_PROTO_UDP))) {
                if (coap_dtls_is_supported() && (key_defined || cert_file)) {
                    ep_dtls = coap_new_endpoint(ctx, &addrs, COAP_PROTO_DTLS);
                    if (!ep_dtls)
                        coap_log(LOG_CRIT, "cannot create DTLS endpoint\n");
                }
                goto finish;
            } else {
                coap_log(LOG_CRIT, "cannot create UDP endpoint\n");
                continue;
            }
        }
    }
finish:
  freeaddrinfo(result);
  return;
}


void
init_resources(coap_context_t *ctx) {
  coap_resource_t *r;

  r = coap_resource_init(NULL, 0);
  coap_register_handler(r, COAP_REQUEST_GET, hnd_get_index);

  coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0);
  coap_add_attr(r, coap_make_str_const("title"), coap_make_str_const("\"General Info\""), 0);
  coap_add_resource(ctx, r);

  r = coap_resource_init(coap_make_str_const("data"), resource_flags);
  coap_register_handler(r, COAP_REQUEST_PUT, hnd_put_data);
  coap_add_resource(ctx, r);

  /* store clock base to use in /time */
  my_clock_base = clock_offset;

  r = coap_resource_init(coap_make_str_const("time"), resource_flags);
  coap_register_handler(r, COAP_REQUEST_GET, hnd_get_time);
  coap_register_handler(r, COAP_REQUEST_PUT, hnd_put_time);
  coap_register_handler(r, COAP_REQUEST_DELETE, hnd_delete_time);
  coap_resource_set_get_observable(r, 1);

  coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0);
  coap_add_attr(r, coap_make_str_const("title"), coap_make_str_const("\"Internal Clock\""), 0);
  coap_add_attr(r, coap_make_str_const("rt"), coap_make_str_const("\"ticks\""), 0);
  coap_add_attr(r, coap_make_str_const("if"), coap_make_str_const("\"clock\""), 0);

  coap_add_resource(ctx, r);
  time_resource = r;

  if (support_dynamic > 0) {
    /* Create a resource to handle PUTs to unknown URIs */
    r = coap_resource_unknown_init(hnd_unknown_put);
    coap_add_resource(ctx, r);
  }
#ifndef WITHOUT_ASYNC
  r = coap_resource_init(coap_make_str_const("async"), 0);
  coap_register_handler(r, COAP_REQUEST_GET, hnd_get_async);

  coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0);
  coap_add_resource(ctx, r);
#endif /* WITHOUT_ASYNC */
}


int event_loop(coap_context_t *ctx)
{
  coap_tick_t now;
  int opt;
  coap_log_t log_level = LOG_WARNING;
  unsigned wait_ms;
  time_t t_last = 0;
  struct sigaction sa;

/* regeister signal can be called, when kill pid 
  to control coap loop events on switch
*/
  memset (&sa, 0, sizeof(sa));
  sigemptyset(&sa.sa_mask);
  sa.sa_handler = handle_sigint;
  sa.sa_flags = 0;
  sigaction (SIGINT, &sa, NULL);
  sigaction (SIGTERM, &sa, NULL);

  wait_ms = COAP_RESOURCE_CHECK_TIME * 1000;

  while ( !quit ) {
    int result = coap_run_once( ctx, wait_ms );
    if ( result < 0 ) {
      break; /* error happend */
    } else if ( result && (unsigned)result < wait_ms ) {
      /* part of events deal end */
      /* : decrement if there is a result wait time returned */
      wait_ms -= result;
    } else {
      /* timeout:
       *
       * result == 0, or result >= wait_ms
       * (wait_ms could have decremented to a small value, below
       * the granularity of the timer in coap_run_once() and hence
       * result == 0)
       */
      time_t t_now = time(NULL);
      if (t_last != t_now) {
        /* Happens once per second */
        int i;
        t_last = t_now;
      }
      if (result) { /* once coap events deal completed */
        /* result must have been >= wait_ms, so reset wait_ms */
        wait_ms = COAP_RESOURCE_CHECK_TIME * 1000;
      }
    }

#ifndef WITHOUT_ASYNC
    /* check if we have to send asynchronous responses */
    coap_ticks( &now );
    check_async(ctx, now);
#endif /* WITHOUT_ASYNC */
  }

  coap_free_context(ctx);
  coap_cleanup();

  return 0;
}


